package de.karnik.libkara.roster;

import java.util.Vector;

import de.karnik.libjotr.Jotr;
import de.karnik.libjotr.JotrConstants;
import de.karnik.libjotr.JotrContext;
import de.karnik.libjotr.JotrProtocolException;
import de.karnik.libjotr.JotrListener;
import de.karnik.helper.LogFunctions;
import de.karnik.libkara.pluginspecs.BaseProtocol;

/**
 * The contact class implements the functionality for one contact, the contact messages, the otr context and other stuff.
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class ImContact implements JotrListener {
	
	//
	// JOTR Stuff
	//
	/**
	 * The jOTR instance.
	 */
	private Jotr jotr = Jotr.getInstace();
	/**
	 * The jOTR context object.
	 */
	private JotrContext jotrContext = null;
	
	/**
	 * The new incoming message count.
	 */
	private int newIncomingMessageCount = 0;
	/**
	 * The message queue for all messages.
	 */
	private Vector<ImMessage> messageQueue = new Vector<ImMessage>();
	/**
	 * The user id.
	 */
	private String userID = null;
	/**
	 * The user name.
	 */
	private String userName = null;
	/**
	 * The contact groups.
	 */
	private String groups[] = null;
	/**
	 * The contact subscription.
	 */
	private String subscription = null;
	/**
	 * The current contact status message.
	 */
	private String userStatusMessage = null;
	/**
	 * The secure connection indicator.
	 */
	private boolean secure = false;
	/**
	 * The current contact status.
	 */
	private int userStatus = BaseProtocol.STATUS_OFFLINE;
	/**
	 * The contact listeners.
	 */
	private Vector<ImContactListener> imContactListener = new Vector<ImContactListener>();
	
	/**
	 * Constructs a contact object from the given values.
	 * 
	 * @param userID The contacts user id.
	 * @param userName The contacts user name.
	 * @param groups The contacts groups.
	 */
	public ImContact( String userID, String userName, String[] groups ) {
		try {
			this.jotrContext = jotr.createNewContext();
		} catch ( Exception e ) {
			LogFunctions.log( "ImContact - Constructor" , "Exception", e.getMessage() );
		}
		
		this.jotr.addJotrListener( this );
		this.setUserID( userID );
		this.setUserName( userName );
		this.setGroups( groups );
	}
	
	/**
	 * Addss a contact listener.
	 * 
	 * @param listener The contact listener to add.
	 */
	public void addContactListener( ImContactListener listener ){
		this.imContactListener.add( listener );
	}
	
	/**
	 * Returns all messages.
	 * 
	 * @return All messages.
	 */
	public ImMessage[] getMessages() {
		return this.messageQueue.toArray( new ImMessage[ messageQueue.size() ] );
	}
	
	/**
	 * Adds incoming message.
	 * 
	 * @param message The incoming message to add.
	 */
	public void addIncomingMessage( ImMessage message ) {	 
		int status = -1;
		try {
			status = this.jotr.processIncomingMessage( jotrContext, message.getMessage() );
			
		} catch ( JotrProtocolException e ) {

			LogFunctions.log( "ImContact.addIncomingMessage", "Exception", e.getMessage() );
			
			addSystemMessage( new ImMessage( new StringBuffer( "Error: " + e.getLocalMessage() ) ) );
			addOutgoingOtrMessage( Jotr.MESSAGE_TYPE_ERROR_MSG, e.getRemoteMessage() );
			
			return;
		} catch ( Exception e ) {
			
			LogFunctions.log( "ImContact.addIncomingMessage", "Exception", e.getMessage() );
			LogFunctions.log( "ImContact.addIncomingMessage", "Exception", e.getStackTrace() );
			
			return;
		}

		// set communication status
		if( status == Jotr.MESSAGE_ENCRYPTED )
			secure = true;
		else
			secure = false;
		
		if( status != Jotr.MESSAGE_IGNORE ) {
			message.setTimeStamp( System.currentTimeMillis() );
			
			if( status == Jotr.MESSAGE_ERROR )
				message.setType( ImMessage.SYSTEM );
			else 
				message.setType( ImMessage.INCOMING );
			
			messageQueue.add( message );
			newIncomingMessageCount++;
			
			for( int i = 0; i < imContactListener.size(); i++ ) {
				imContactListener.get( i ).messageArrived();
				imContactListener.get( i ).incomingMessageArrived( this, message );
			}
		}
	}
	
	/**
	 * Adds system message.
	 * 
	 * @param message The system message to add.
	 */
	private void addSystemMessage( ImMessage message ) {
		message.setType( ImMessage.SYSTEM );
		message.setTimeStamp( System.currentTimeMillis() );
		messageQueue.add( message );
		
		for( int i = 0; i < imContactListener.size(); i++ )
			imContactListener.get( i ).messageArrived();
	}
	
	/**
	 * Adds outgoing otr message.
	 * 
	 * @param type The otr message type.
	 * 
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_DH_COMMIT_MSG
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_DH_KEY_MSG
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_REVEAL_SIG_MSG
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_SIG_MSG
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_QUERY_MSG
	 */
	private void addOutgoingOtrMessage( int type ) {
		addOutgoingOtrMessage( type, null );
	}
	
	/**
	 * Adds outgoing otr message.
	 * 
	 * @param type The otr message type.
	 * @param info Additional info for the otr message.
	 * 
	 * @see de.karnik.jotr.JotrConstants#MESSAGE_TYPE_ERROR_MSG
	 */
	private void addOutgoingOtrMessage( int type, String info ) {
		// create empty message to hold system information
		
		ImMessage message = new ImMessage( new StringBuffer() );
		message.setTimeStamp( System.currentTimeMillis() );
		
		if( info != null )
			message.getMessage().append( info );
		
		// append auth message ( if type is auth message type )
		jotr.createOTRProtoMessage( type, jotrContext, message.getMessage(), null );
		// TODO get username
		
		for( int i = 0; i < imContactListener.size(); i++ )
			imContactListener.get( i ).outgoingMessageArrived( this, message );
	}
	
	/**
	 * Adds outgoing message.
	 *  
	 * @param message The outgoing message to add.
	 */
	public void addOutgoingMessage( ImMessage message ) {
		try {
				message.setTimeStamp( System.currentTimeMillis() );
				message.setType( ImMessage.OUTGOING );
				this.messageQueue.add( message );
				
				ImMessage otrMsg = message.clone(); 
				
				this.jotr.processOutgoingMessage( jotrContext, otrMsg.getMessage() );
				
				for( int i = 0; i < imContactListener.size(); i++ ) {
					imContactListener.get( i ).messageArrived();
					imContactListener.get( i ).outgoingMessageArrived( this, otrMsg );
				}

		} catch ( Exception e ) {
			LogFunctions.log( "ImContact.addOutgoingMessage", "Exception", e.getMessage() );
			LogFunctions.log( "ImContact.addOutgoingMessage", "Exception", e.getStackTrace() );
		}

	}

	/**
	 * Returns the user id.
	 * 
	 * @return The user id.
	 */
	public String getUserID() {
		return userID;
	}
	
	/**
	 * Sets the user id.
	 * 
	 * @param userID The user id to set.
	 */
	public void setUserID( String userID ) {
		this.userID = userID;
	}
	
	/**
	 * Returns all groups.
	 * 
	 * @return All available groups.
	 */
	public String[] getGroups() {
		return groups;
	}
	
	/**
	 * Sets the groups.
	 * 
	 * @param groups The groups to set.
	 */
	public void setGroups( String[] groups ) {
		this.groups = groups;
	}
	
	/**
	 * Returns the current user status.
	 * 
	 * @return The current user status.
	 */
	public int getUserStatus() {
		return userStatus;
	}
	
	/**
	 * Sets the current user status.
	 * 
	 * @param userStatus The current user status to set.
	 */
	public void setUserStatus( int userStatus ) {
		this.userStatus = userStatus;
	}

	/**
	 * Returns the user name.
	 * 
	 * @return The user name.
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * Sets the user name.
	 * 
	 * @param userName The user name to set.
	 */
	public void setUserName( String userName ) {
		this.userName = userName;
	}

	/**
	 * Returns the contacts subscription type.
	 * 
	 * @return The contacts subscription type.
	 */
	public String getSubscription() {
		return subscription;
	}

	/**
	 * Sets the contacts subscription type.
	 * 
	 * @param subscription The contacts subscription type.
	 */
	public void setSubscription( String subscription ) {
		this.subscription = subscription;
	}

	/**
	 * Returns the current user status message.
	 * 
	 * @return The current user status message.
	 */
	public String getUserStatusMessage() {
		return userStatusMessage;
	}

	/**
	 * Sets the user status message.
	 * 
	 * @param userStatusMessage The user status message.
	 */
	public void setUserStatusMessage( String userStatusMessage ) {
		this.userStatusMessage = userStatusMessage;
	}

	/**
	 * Returns the new incoming message count value.
	 * 
	 * @return The new incoming message count value.
	 */
	public int getNewIncomingMessageCount() {
		return newIncomingMessageCount;
	}

	/**
	 * Resets the new incoming message count value to 0.
	 */
	public void resetNewIncomingMessageCount() {
		this.newIncomingMessageCount = 0;
	}
	
	/**
	 * Returns the security status.
	 * 
	 * @return The security status.
	 */
	public boolean isSecure() {
		return secure;
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#messageWasReceivedUnencrypted(de.karnik.jotr.JotrContext)
	 */
	public void messageWasReceivedUnencrypted( JotrContext context, String message ) {
		if( context.equals( jotrContext ) ) {
			
			LogFunctions.log( "ImContact.messageWasReceivedUnencrypted", "addSystemMessage", message );
			
			addSystemMessage( new ImMessage( new StringBuffer( JotrConstants.OTR_MESSAGE_UNECRYPTED + message ) ) );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhCommitMessageRequested(de.karnik.jotr.JotrContext)
	 */
	public void dhCommitMessageRequested( JotrContext context ) {
	
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhCommitMessageRequested", "addOutgoingOtrMessage" );
		
			for( int i = 0; i < imContactListener.size(); i++ )
				imContactListener.get( i ).authProcessStarted();
		
			addOutgoingOtrMessage( JotrConstants.MESSAGE_TYPE_DH_COMMIT_MSG );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhKeyMessageRequested(de.karnik.jotr.JotrContext)
	 */
	public void dhKeyMessageRequested( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhKeyMessageRequested", "addOutgoingOtrMessage" );

			for( int i = 0; i < imContactListener.size(); i++ )
				imContactListener.get( i ).authProcessStarted();
		
			addOutgoingOtrMessage( JotrConstants.MESSAGE_TYPE_DH_KEY_MSG );
		}	
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhRevealSignatureMessageRequested(de.karnik.jotr.JotrContext)
	 */
	public void dhRevealSignatureMessageRequested( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhRevealSignatureMessageRequested", "addOutgoingOtrMessage" );
			addOutgoingOtrMessage( JotrConstants.MESSAGE_TYPE_REVEAL_SIG_MSG );
		}	
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhSignatureMessageRequested(de.karnik.jotr.JotrContext)
	 */
	public void dhSignatureMessageRequested( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhSignatureMessageRequested", "addOutgoingOtrMessage" );
			addOutgoingOtrMessage( JotrConstants.MESSAGE_TYPE_SIG_MSG );
		}		
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#authentificationFinished(de.karnik.jotr.JotrContext)
	 */
	public void authentificationFinished( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			
			LogFunctions.log( "ImContact.authentificationFinished", "addSystemMessage" );
			
			addSystemMessage( new ImMessage( new StringBuffer( "Auth finished..." ) ) );
		
			for( int i = 0; i < imContactListener.size(); i++ )
				imContactListener.get( i ).authProcessFinished();
		}
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#authentificationStarted(de.karnik.jotr.JotrContext)
	 */
	public void authentificationStarted( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			
			LogFunctions.log( "ImContact.authentificationStarted", "addSystemMessage" );
			
			addSystemMessage( new ImMessage( new StringBuffer( "Auth started..." ) ) );
			
			for( int i = 0; i < imContactListener.size(); i++ )
				imContactListener.get( i ).authProcessStarted();
		}
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#errorMessageReceived(de.karnik.jotr.JotrContext, java.lang.String)
	 */
	public void errorMessageReceived( JotrContext context, String message ) {
		if( context.equals( jotrContext ) ) {
			
			LogFunctions.log( "ImContact.errorMessageReceived", "addSystemMessage", message );
			
			addSystemMessage( new ImMessage( new StringBuffer( "Error: " + message ) ) );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#queryMessageRequested(de.karnik.jotr.JotrContext)
	 */
	public void queryMessageRequested( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			
			LogFunctions.log( "ImContact.queryMessageRequested", "addOutgoingOtrMessage" );
			
			addOutgoingOtrMessage( JotrConstants.MESSAGE_TYPE_QUERY_MSG );
		}
	}
	
	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhCommitMessageReceived(de.karnik.jotr.JotrContext)
	 */
	public void dhCommitMessageReceived( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhCommitMessageReceived", "dhCommitMessageReceived" );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhKeyMessageReceived(de.karnik.jotr.JotrContext)
	 */
	public void dhKeyMessageReceived( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhKeyMessageReceived", "dhKeyMessageReceived" );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhRevealSignatureMessageReceived(de.karnik.jotr.JotrContext)
	 */
	public void dhRevealSignatureMessageReceived( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhRevealSignatureMessageReceived", "dhRevealSignatureMessageReceived" );
		}
	}

	/* (non-Javadoc)
	 * @see de.karnik.jotr.JotrListener#dhSignatureMessageReceived(de.karnik.jotr.JotrContext)
	 */
	public void dhSignatureMessageReceived( JotrContext context ) {
		if( context.equals( jotrContext ) ) {
			LogFunctions.log( "ImContact.dhSignatureMessageReceived", "dhSignatureMessageReceived" );
		}
	}
}
